package com.whale.kernel.service.hibernate;

import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.xml.stream.XMLInputFactory;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

import com.whale.kernel.service.hibernate.transformer.Scalar;
import com.whale.kernel.service.hibernate.transformer.ScalarEntity;
import com.whale.kernel.service.hibernate.transformer.ScalarEntityListTransformer;
import com.whale.kernel.service.hibernate.transformer.ScalarEntityMapTransformer;
import com.whale.kernel.service.hibernate.transformer.ScalarEntityTransformer;

public abstract class AbstractHibernateService {

	protected HibernateTemplate hibernateTemplate;

	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
		initialize();
	}

	/**
	 * Override this function if you want to initialize additional
	 * 
	 * @param dataSource
	 */
	protected void initialize() {
	}

	@SuppressWarnings("unchecked")
	protected <T> List<T> getQueryResult(Class<T> clazz, final String strQuery,
			final Map<String, Object> parameters, final Integer firstResult,
			final Integer maxResult) {
		return hibernateTemplate.executeFind(new HibernateCallback<List<T>>() {

			public List<T> doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query query = session.createQuery(strQuery);
				if (parameters != null) {
					setQueryParameters(parameters, query);
				}
				if (firstResult != null && maxResult != null) {
					query.setFirstResult(firstResult).setMaxResults(maxResult);
				}
				return query.list();
			}
		});
	}

	protected <T> List<T> getQueryResult(Class<T> clazz, final String strQuery,
			final Map<String, Object> parameters) {
		return getQueryResult(clazz, strQuery, parameters, null, null);
	}

	protected <T> List<T> getQueryResult(Class<T> clazz, final String strQuery) {
		return getQueryResult(clazz, strQuery, null);
	}

	protected <T> T getUniqueQueryResult(Class<T> clazz, final String strQuery,
			final Map<String, Object> parameters) {
		return hibernateTemplate.execute(new HibernateCallback<T>() {

			@SuppressWarnings("unchecked")
			public T doInHibernate(Session session) throws HibernateException,
					SQLException {
				Query query = session.createQuery(strQuery);
				if (parameters != null) {
					setQueryParameters(parameters, query);
				}
				return (T) query.uniqueResult();
			}
		});
	}

	protected <T> T getUniqueQueryResult(Class<T> clazz, final String strQuery) {
		return getUniqueQueryResult(clazz, strQuery, null);
	}

	protected void executeUpdate(final String strQuery,
			final Map<String, Object> parameters) {
		hibernateTemplate.execute(new HibernateCallback<Object>() {

			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query query = session.createQuery(strQuery);
				if (parameters != null) {
					setQueryParameters(parameters, query);
				}
				query.executeUpdate();
				return null;
			}
		});
	}

	protected void executeUpdate(final String strQuery) {
		executeUpdate(strQuery, null);
	}

	@SuppressWarnings("unchecked")
	protected void setQueryParameters(Map<String, Object> values, Query query) {
		for (String key : values.keySet()) {
			Object val = values.get(key);
			if (val instanceof Collection) {
				// в данном случае параметр вида in (:list)
				Collection<Object> vals = (Collection<Object>) val;
				query.setParameterList(key, vals);
			} else {
				query.setParameter(key, values.get(key));
			}
		}
	}

	protected XMLInputFactory createXMLInputFactory() {
		XMLInputFactory xmlif = XMLInputFactory.newInstance();
		xmlif.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
				Boolean.TRUE);
		xmlif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
				Boolean.FALSE);

		// Important:
		xmlif.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
		return xmlif;
	}

	protected <T> List<T> getNativeQueryResult(Class<T> entityClazz,
			final String strQuery, final Map<String, Object> parameters) {
		return getNativeQueryResult(entityClazz, strQuery, parameters, null,
				null);
	}

	protected <T> List<T> getNativeQueryResult(Class<T> entityClazz,
			final String strQuery) {
		return getNativeQueryResult(entityClazz, strQuery, null);
	}

	/**
	 * Выполняет Native запрос
	 */
	@SuppressWarnings("unchecked")
	protected <T> List<T> getNativeQueryResult(final Class<T> entityClazz,
			final String strQuery, final Map<String, Object> parameters,
			final Integer firstResult, final Integer maxResult) {
		return hibernateTemplate
				.executeWithNativeSession(new HibernateCallback<List<T>>() {

					public List<T> doInHibernate(Session session)
							throws HibernateException, SQLException {
						SQLQuery query = session.createSQLQuery(strQuery);
						query.addEntity(entityClazz);

						if (parameters != null) {
							setQueryParameters(parameters, query);
						}
						if (firstResult != null && maxResult != null) {
							query.setFirstResult(firstResult).setMaxResults(
									maxResult);
						}
						return query.list();
					}
				});
	}

	protected <T, E extends ScalarEntity<T>> Map<Long, E> getNativeQueryResult(
			final ScalarEntityMapTransformer<T, E> transformer,
			final String strQuery, final Map<String, Object> parameters) {
		return getNativeQueryResult(transformer, strQuery, parameters, null,
				null);
	}
	
	protected <T, E extends ScalarEntity<T>> List<E> getNativeQueryResult(
			final ScalarEntityListTransformer<T, E> transformer,
			final String strQuery, final Map<String, Object> parameters) {
		return getNativeQueryResult(transformer, strQuery, parameters, null,
				null);
	}

	/**
	 * Выполняет Native запрос в результате которого имеется доп. информация не
	 * содержащаяся в Entity
	 */
	protected <T, E extends ScalarEntity<T>> Map<Long, E> getNativeQueryResult(
			final ScalarEntityMapTransformer<T, E> transformer,
			final String strQuery, final Map<String, Object> parameters,
			final Integer firstResult, final Integer maxResult) {
		List<Object[]> list = getScalarEntityNativeQueryResult(transformer,
				strQuery, parameters, firstResult, maxResult);
		return transformer.transformResultsToMap(list);
	}

	/**
	 * Выполняет Native запрос в результате которого имеется доп. информация не
	 * содержащаяся в Entity
	 */
	protected <T, E extends ScalarEntity<T>> List<E> getNativeQueryResult(
			final ScalarEntityListTransformer<T, E> transformer,
			final String strQuery, final Map<String, Object> parameters,
			final Integer firstResult, final Integer maxResult) {
		List<Object[]> list = getScalarEntityNativeQueryResult(transformer,
				strQuery, parameters, firstResult, maxResult);
		return transformer.transformResultsToList(list);
	}

	@SuppressWarnings("unchecked")
	private <T, E extends ScalarEntity<T>> List<Object[]> getScalarEntityNativeQueryResult(
			final ScalarEntityTransformer<T, E> transformer,
			final String strQuery, final Map<String, Object> parameters,
			final Integer firstResult, final Integer maxResult) {
		return hibernateTemplate
				.executeWithNativeSession(new HibernateCallback<List<Object[]>>() {
					public List<Object[]> doInHibernate(Session session)
							throws HibernateException, SQLException {
						SQLQuery query = session.createSQLQuery(strQuery);
						query.addEntity(transformer.getEntityClass());
						for (Scalar scalar : transformer.getScalars()) {
							query.addScalar(scalar.getColumnAlias(),
									scalar.getType());
						}

						if (parameters != null) {
							setQueryParameters(parameters, query);
						}
						if (firstResult != null && maxResult != null) {
							query.setFirstResult(firstResult).setMaxResults(
									maxResult);
						}
						return query.list();
					}
				});
	}
}
